package chatsystem.ni;

import chatsystem.util.BidiMap;
import chatsystem.util.Pair;
import chatsystemTDa2.*;
import java.net.InetAddress;
import java.net.SocketException;

/** Receiver thread for ChatNI. 
 *  Stop the thread by socket close. */
public class Receiver extends Thread
{

   Receiver(ChatNI owner)
   {
      _owner = owner;
      
      _backend = owner.getBackend();
      _sock = owner.getSocket();
      _addrMap = owner.getAddrMap();
   }

   /** Gère l'arrivée d'un nouveau peer. Peut être compliqué si les gens
    *  n'envoient pas les Goodbye. */
   private void onNewPeer(String name, InetAddress addr)
   {
      if (_addrMap.hasKey(name)) {
         // Même utilisateur se reconnecte sur une autre machine
         // (normalement impossible avec le hack du '@IP')
         _addrMap.removeByKey(name);
         _backend.onPeerDisconnect(name);
      }

      if (_addrMap.hasValue(addr)) {
         // qqun se reconnecte, alors qu'il n'avait pas envoyé de Goodbye
         _backend.onPeerDisconnect(_addrMap.getByValue(addr));
         _addrMap.removeByValue(addr);
      }

      _addrMap.insert(name, addr);
      _backend.onNewPeer(name);
   }

   public void run()
   {
      while (true)
      {
         try {
            Pair<InetAddress, Message> p = _owner.getSocket().recvFrom();
            processMsg(p.second, p.first);
            System.out.println(p.second);
         } catch (SocketException e) {
            break; // stop by socket close
         } catch (Throwable e) {
            e.printStackTrace();
            // log and continue
         }
      }
   }

   private void processMsg(Message m, InetAddress sender)
   {
      // Hello's: traités immédiatement
      if (m instanceof Hello) {
         String nick = ((Hello)m).getNickname()+"@"+sender.getHostAddress();
         onNewPeer(nick, sender);
         // Répond avec un HelloAck
         _sock.sendTo(sender, new HelloAck(_owner.getMyNickname()));
      }
      else if (m instanceof HelloAck) {
         String nick = ((HelloAck)m).getNickname()+"@"+sender.getHostAddress();
         onNewPeer(nick, sender);
      }
      
      // messages: traités immédiatement
      else if (m instanceof Send) {
         String msg = ((Send)m).getMessage();
         int mID = ((Send)m).getID();
         _sock.sendTo(sender, new SendAck(mID));
         _backend.onMessage(_addrMap.getByValue(sender), msg);
      }
      else if (m instanceof SendAck) {
         // packet loss not handled yet.
      }
      
      // Fichiers: délégués au ChatNI
      else if (m instanceof FileRequest) {
         String filename = ((FileRequest)m).getName();
         _owner.onFileRecv(sender, filename);
      }
      else if (m instanceof FileResponse) {
         boolean sayYes = ((FileResponse)m).getResponse();
         _owner.onFileResponse(sayYes);
      }
      
      // Goodbye: traité immédiatement
      else if (m instanceof Goodbye) {
         String name = _addrMap.getByValue(sender);
         _addrMap.removeByValue(sender);
         _backend.onPeerDisconnect(name);
      }
      
      else {
         System.err.println("[NI] Unrecognized message type: "+m);
      }
   }

/******************************************************************************/
   private ChatNI _owner;
   
   // all borrowed
   private FromNI _backend;
   private Socket _sock;
   private BidiMap<String, InetAddress> _addrMap;
}
